package com.example.portal.utils;

import com.example.portal.model.SVNModel;
import org.apache.log4j.Logger;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryFactory;
import org.tmatesoft.svn.core.internal.io.svn.SVNRepositoryFactoryImpl;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.*;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.nio.charset.Charset;
import java.util.*;

public class SvnUtil {

    private static Logger logger = Logger.getLogger(SvnUtil.class);
    private SVNClientManager manager;
    private SVNUpdateClient updateClient;
    private String url;
    private String userName;
    private String passwd;
    private SVNRepository repository;
    public SvnUtil(String userName, String passwd) {
        init(userName, passwd);
    }

    public SvnUtil(String userName, String passwd, String url){
        this(userName,passwd);
        this.url=url;
    }

    private void init(String userName,String passwd){
        DefaultSVNOptions options = new DefaultSVNOptions();
        manager = SVNClientManager.newInstance(options);
        manager = SVNClientManager.newInstance(options,userName,passwd);
        updateClient = manager.getUpdateClient();
        updateClient.setIgnoreExternals(false);
    }


    /**获取文档内容
     * @param url
     * @return
     */
    public String checkoutFileToString(String url) throws SVNException {//"", -1, null
        repository = createRepository(url);
        SVNDirEntry entry = repository.getDir("", -1, false, null);
        int size = (int)entry.getSize();
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream(size);
        SVNProperties properties = new SVNProperties();
        repository.getFile("", -1, properties, outputStream);
        String doc = new String(outputStream.toByteArray(), Charset.forName("utf-8"));
        return doc;
    }

    public boolean toParantFolder(){
        if(url!=null){
            StringBuffer sb = new StringBuffer(url);
            if(url.endsWith("/")){
                sb.deleteCharAt(sb.length()-1);
            }
            int index = sb.lastIndexOf("/");
            url=sb.substring(0, index);
            return true;
        }
        return false;
    }

    /**进入子目录
     * @param folder
     * @return
     */
    public boolean toChildFolder(String folder){
        if(url!=null){
            StringBuffer sb = new StringBuffer(url);
            boolean a = url.endsWith("/");
            boolean b = folder.startsWith("/");
            if(a^b){
                sb.append(folder);
            }else if(a&b){
                sb.deleteCharAt(sb.length()-1);
                sb.append(folder);
            }else{
                sb.append('/').append(folder);
            }
            if(checkPath(sb.toString())==1){
                this.url=sb.toString();
                return true;
            }
        }
        return false;
    }

    /**获取当前目录下的子目录和文件
     * @return
     * @throws SVNException
     */
    public List<SVNDirEntry> listFolder() throws SVNException {
        return listFolder(url);
    }

    /**
     * 获取当前目录下的子目录和文件简化数据
     * @param sort:0:不排序，1：按name名称排序
     * @return
     * @throws SVNException
     */
    public List<SVNModel> getSVNSimplifyData(Integer sort) throws SVNException {
        List<SVNModel> data = new ArrayList<>();
        List<SVNDirEntry> list = listFolder();
        if(list != null && list.size() > 0) {
            if(sort == 1) {
                Collections.sort(list, Comparator.comparing(SVNDirEntry::getName));
            }
            Integer index = 0;
            for (SVNDirEntry entry : list) {
                if(entry.getKind().getID() == 0) {
                    SVNModel model = new SVNModel(index, entry.getName(), entry.getURL().getPath(), entry.getKind().getID());
                    data.add(model);
                    index++;
                }
            }

            for (SVNDirEntry entry : list) {
                if(entry.getKind().getID() == 1) {
                    SVNModel model = new SVNModel(index, entry.getName(), entry.getURL().getPath(), entry.getKind().getID());
                    data.add(model);
                    index++;
                }
            }
        }
        return data;
    }

    /**列出指定SVN 地址目录下的子目录
     * @param url
     * @return
     * @throws SVNException
     */
    public List<SVNDirEntry> listFolder(String url) throws SVNException{
        if(checkPath(url)==1){

            repository = createRepository(url);
            try {
                Collection<SVNDirEntry> list = repository.getDir("", -1, null, (List<SVNDirEntry>)null);
                List<SVNDirEntry> dirs = new ArrayList<>(list.size());
                dirs.addAll(list);
                return dirs;
            } catch (SVNException e) {
                logger.error("listFolder error",e);
            }

        }
        return null;
    }

   public SVNRepository createRepository(String url){

        try {
            return manager.createRepository(SVNURL.parseURIEncoded(url), true);
        } catch (SVNException e) {
            logger.error("createRepository error",e);
        }
        return null;
    }

    public String getSvnRoot()
    {
        SVNURL url = null;
        try
        {
            url = this.repository.getRepositoryRoot(false);
            return url.getPath();
        }
        catch (SVNException e)
        {
            e.printStackTrace();
            logger.error("You have no right get this repository root url !!!");
        }
        return null;
    }

    public String[] getCodePaths(String url)
    {
        String codepath = getSvnRoot();
        int homeIndex = url.indexOf(codepath);
        return new String[] { url.substring(homeIndex + codepath.length()) };
    }

    public SVNDirEntry getSVNInfo(String svnUrl) throws SVNException{
        repository = createRepository(url);
        String[] codepath = getCodePaths(svnUrl);
        SVNDirEntry collect = null;
        try
        {
            collect = this.repository.info(codepath[0], -1  );
        }
        catch (SVNException e)
        {
            collect = null;
            logger.error(e.getMessage());
        }
        return collect;
    }

    /**检查路径是否存在
     * @param url
     * @return 1：存在    0：不存在   -1：出错
     */
    public int checkPath(String url){
        repository = createRepository(url);
        SVNNodeKind nodeKind;
        try {
            nodeKind = repository.checkPath("", -1);
            boolean result = nodeKind == SVNNodeKind.NONE ? false : true;
            if(result) return 1;
        } catch (SVNException e) {
            logger.error("checkPath error",e);
            return -1;
        }
        return 0;
    }

    public String getUrl() {
        return url;
    }
    public void setUrl(String url) {
        this.url = url;
    }
    public String getUserName() {
        return userName;
    }
    public void setUserName(String userName) {
        this.userName = userName;
    }
    public String getPasswd() {
        return passwd;
    }
    public void setPasswd(String passwd) {
        this.passwd = passwd;
    }

    /**
     * 通过不同的协议初始化版本库
     */
    public static void setupLibrary() {
        DAVRepositoryFactory.setup();
        SVNRepositoryFactoryImpl.setup();
        FSRepositoryFactory.setup();
    }

    /**
     * 验证登录svn
     */
    public static SVNClientManager authSvn(String svnRoot, String username,
                                           String password) {
        // 初始化版本库
        setupLibrary();

        // 创建库连接
        SVNRepository repository = null;
        try {
            repository = SVNRepositoryFactory.create(SVNURL
                    .parseURIEncoded(svnRoot));
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
            return null;
        }

        // 身份验证
        ISVNAuthenticationManager authManager = SVNWCUtil

                .createDefaultAuthenticationManager(username, password);

        // 创建身份验证管理器
        repository.setAuthenticationManager(authManager);

        DefaultSVNOptions options = SVNWCUtil.createDefaultOptions(true);
        SVNClientManager clientManager = SVNClientManager.newInstance(options,
                authManager);
        return clientManager;
    }

    /**
     * 在SVN仓库中创建目录
     * Make directory in svn repository
     * @param clientManager
     * @param url
     *          eg: http://svn.ambow.com/wlpt/bsp/trunk
     * @param commitMessage
     * @return
     * @throws SVNException
     */
    public static SVNCommitInfo makeDirectory(SVNClientManager clientManager,
                                              SVNURL url, String commitMessage) {
        try {
            return clientManager.getCommitClient().doMkDir(
                    new SVNURL[] { url }, commitMessage);
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
        }
        return null;
    }

    /**
     * 将没有版本的目录导入到库中
     * Imports an unversioned directory into a repository location denoted by a
     *  destination URL
     * @param clientManager
     * @param localPath
     *          a local unversioned directory or singal file that will be imported into a
     *          repository;
     * @param dstURL
     *          a repository location where the local unversioned directory/file will be
     *          imported into
     * @param commitMessage
     * @param isRecursive 递归
     * @return
     */
    public static SVNCommitInfo importDirectory(SVNClientManager clientManager,
                                                File localPath, SVNURL dstURL, String commitMessage,
                                                boolean isRecursive) {
        try {
            return clientManager.getCommitClient().doImport(localPath, dstURL,
                    commitMessage, null, true, true,
                    SVNDepth.fromRecurse(isRecursive));
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
        }
        return null;
    }

    /**
     * 将目录和文件置于版本控制之下
     * Puts directories and files under version control
     * @param clientManager
     *          SVNClientManager
     * @param wcPath
     *          work copy path
     */
    public static void addEntry(SVNClientManager clientManager, File wcPath) {
        try {
            clientManager.getWCClient().doAdd(new File[] { wcPath }, true,
                    false, false, SVNDepth.INFINITY, false, false,
                    true);
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
        }
    }

    /**
     * 收集单个工作副本项目的状态信息
     * Collects status information on a single Working Copy item
     * @param clientManager
     * @param wcPath
     *          local item's path
     * @param remote
     *          true to check up the status of the item in the repository,
     *          that will tell if the local item is out-of-date (like '-u' option in the SVN client's
     *          'svn status' command), otherwise false
     * @return
     * @throws SVNException
     */
    public static SVNStatus showStatus(SVNClientManager clientManager,
                                       File wcPath, boolean remote) {
        SVNStatus status = null;
        try {
            status = clientManager.getStatusClient().doStatus(wcPath, remote);
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
        }
        return status;
    }

    /**
     * 将版本变化提交到svn
     * Commit work copy's change to svn
     * @param clientManager
     * @param wcPath
     *          working copy paths which changes are to be committed
     * @param keepLocks
     *          whether to unlock or not files in the repository
     * @param commitMessage
     *          commit log message
     * @return
     * @throws SVNException
     */
    public static SVNCommitInfo commit(SVNClientManager clientManager,
                                       File wcPath, boolean keepLocks, String commitMessage) {
        try {
            return clientManager.getCommitClient().doCommit(
                    new File[] { wcPath }, keepLocks, commitMessage, null,
                    null, false, false, SVNDepth.INFINITY);
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
        }
        return null;
    }

    /**
     * 拉取版本变化
     * Updates a working copy (brings changes from the repository into the working copy).
     * @param clientManager
     * @param wcPath
     *          working copy path
     * @param updateToRevision
     *          revision to update to
     * @param depth
     *          update的深度：目录、子目录、文件
     * @return
     * @throws SVNException
     */
    public static long update(SVNClientManager clientManager, File wcPath,
                              SVNRevision updateToRevision, SVNDepth depth) {
        SVNUpdateClient updateClient = clientManager.getUpdateClient();

        /*
         * sets externals not to be ignored during the update
         */
        updateClient.setIgnoreExternals(false);

        /*
         * returns the number of the revision wcPath was updated to
         */
        try {
            return updateClient.doUpdate(wcPath, updateToRevision,depth, false, false);
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
        }
        return 0;
    }

    /**
     * 将工程导入本地
     * recursively checks out a working copy from url into wcDir
     * @param clientManager
     * @param url
     *          a repository location from where a Working Copy will be checked out
     * @param revision
     *          the desired revision of the Working Copy to be checked out
     * @param destPath
     *          the local path where the Working Copy will be placed
     * @param depth
     *          checkout的深度，目录、子目录、文件
     * @return
     * @throws SVNException
     */
    public static long checkout(SVNClientManager clientManager, SVNURL url,
                                SVNRevision revision, File destPath, SVNDepth depth) {

        SVNUpdateClient updateClient = clientManager.getUpdateClient();
        /*
         * sets externals not to be ignored during the checkout
         */
        updateClient.setIgnoreExternals(false);
        /*
         * returns the number of the revision at which the working copy is
         */
        try {
            return updateClient.doCheckout(url, destPath, revision, revision,depth, false);
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
        }
        return 0;
    }

    /**
     * 确定path是否是一个工作空间
     * @param path
     * @return
     */
    public static boolean isWorkingCopy(File path){
        if(!path.exists()){
            logger.warn("'" + path + "' not exist!");
            return false;
        }
        try {
            if(null == SVNWCUtil.getWorkingCopyRoot(path, false)){
                return false;
            }
        } catch (SVNException e) {
            logger.error(e.getErrorMessage(), e);
        }
        return true;
    }

    /**
     * 确定一个URL在SVN上是否存在
     * @param url
     * @return
     */
    public static boolean isURLExist(SVNURL url,String username,String password){
        try {
            SVNRepository svnRepository = SVNRepositoryFactory.create(url);
            ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(username, password);
            svnRepository.setAuthenticationManager(authManager);
            SVNNodeKind nodeKind = svnRepository.checkPath("", -1);
            return nodeKind == SVNNodeKind.NONE ? false : true;
        } catch (SVNException e) {
            e.printStackTrace();
        }
        return false;
    }

    public static SVNRepository getRepository(String url, String username, String password) {
        DAVRepositoryFactory.setup();
        SVNRepositoryFactoryImpl.setup();
        SVNRepository repository = null;
        SVNNodeKind nodeKind = null;
        try {
            repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded(url));
            ISVNAuthenticationManager authManager = SVNWCUtil.createDefaultAuthenticationManager(username, password);
            repository.setAuthenticationManager(authManager);
            nodeKind = repository.checkPath("", -1);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        if (nodeKind == SVNNodeKind.NONE) {
            throw new RuntimeException("There is no entry at '" + url + "'.");
        } else if (nodeKind == SVNNodeKind.FILE) {
            throw new RuntimeException("The entry at '" + url + "' is a file while a directory was expected.");
        }
        return repository;
    }


}
